Storytelling visual con R

Curso formativo para el PDI. Universidad de Castilla-La Mancha

Gema Fernández-Avilés
Isidro Hidalgo

Unidad 3 La elección de un gráfico efectivo. From data to viz

Agenda

  1. Objetivos de aprendizaje

  2. Resultados esperados

  3. Conceptos clave

  4. ¿Qué hemos aprendido?

1. Objetivos de aprendizaje 🎯

  • Comprender la importancia de la visualización

  • Representaciones con {ggplot2}

2. Resultados esperados 🏅

Hexbin plot (starwars data)

3. Conceptos clave 📃

¿Por qué es importante la visualización? 🖼

“The simple graph has brought more information to the data analyst’s mind than any other device.” — John Tukey

DatasauRus

library(datasauRus)
library(dplyr)

datasaurus_dozen |> 
    group_by(dataset) |> 
    summarize(
      mean_x    = mean(x),
      mean_y    = mean(y),
      std_dev_x = sd(x),
      std_dev_y = sd(y),
      corr_x_y  = cor(x, y)
    )
# A tibble: 13 × 6
  dataset  mean_x mean_y std_dev_x std_dev_y corr_x_y
  <chr>     <dbl>  <dbl>     <dbl>     <dbl>    <dbl>
1 away       54.3   47.8      16.8      26.9  -0.0641
2 bullseye   54.3   47.8      16.8      26.9  -0.0686
3 circle     54.3   47.8      16.8      26.9  -0.0683
4 dino       54.3   47.8      16.8      26.9  -0.0645
5 dots       54.3   47.8      16.8      26.9  -0.0603
6 h_lines    54.3   47.8      16.8      26.9  -0.0617
# ℹ 7 more rows

DatasauRus

Note

Otros ejemplos clásicos son el cuarteto de Anscombe y la paradoja de Simpson.

¿Cómo describirías la relación entre la masa y la altura de los personajes de Starwars?

library(tidyverse)
ggplot(data = starwars, mapping = aes(x = height, y = mass)) +
  geom_point() 

¿Quién es el outlier? Jabba Desilijic Tiure

Imagen adaptada de https://datasciencebox.org

“The grammar of graphics”: {ggplot}

Imagen adaptada de https://datasciencebox.org

  • {ggplot2} es un paquete tidyverse para visualización de datos.

  • gg en ggplot2 hace referencia a Grammar of Graphics.

  • Inspirado en el libro Grammar of Graphics, Wilkinson (2012).

¿Cómo visualizar con la función ggplot() de {ggplot}?

library(ggplot2)
ggplot(data = [dataset], 
       mapping = aes(x = [x-variable], y = [y-variable])) +
   geom_xxx() +
   otras opciones

Ahora, de verdad:

library(ggplot2)
library(dplyr)  #cargamos los datos 'starwars'
# se crea un nuevo data.frame sin el outlier (que se elimina)
starwars2 <- filter(starwars, name != "Jabba Desilijic Tiure")
ggplot(data = starwars2, mapping = aes(x = height, y = mass)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       x = "Height (cm)", y = "Weight (kg)")

Ayuda con ggplot2: ggplot2.tidyverse.org y Cheatsheets.

Mi primer plot con ggplot()

Fuente: Wikipedia

Paso a paso 🐾

Paso a paso:

Empezamos con el data.frame starwars2

ggplot(data = starwars2) 

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x

ggplot(data = starwars2,
       mapping = aes(x = height))

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass)) 

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass)) + 
  geom_point() 

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) + 
  geom_point()

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour =gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters")

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”
añadimos el subtítulo “by gender”

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       subtitle = "by gender")  

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”
añadimos el subtítulo “Género de los personajes de starwars2”
etiquetamos los ejes x e y como “x =”Height (cm)“, y =”Weight (kg)“, respectivamente

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       subtitle = "by gender",  
       x = "Height (cm)", y = "Weight (kg)")

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”
añadimos el subtítulo “Género de los personajes de starwars2”
etiquetamos los ejes x e y como “x =”Height (cm)“, y =”Weight (kg)“, respectivamente
etiquetamos la leyenda “Species”

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       subtitle = "by gender",  
       x = "Height (cm)", y = "Weight (kg)",
       colour = "Gender") 

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”
añadimos el subtítulo “Género de los personajes de starwars2”
etiquetamos los ejes x e y como “x =”Height (cm)“, y =”Weight (kg)“,
etiquetamos la leyenda”Gender”
añadimos la fuente de los datos.

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       subtitle = "by gender",  
       x = "Height (cm)", y = "Weight (kg)",
       colour = "Gender",
       caption = "Source: dplyr package") 

Paso a paso:

Empezamos con el data.frame starwars2
ponemos height en el eje-x
ponemos mass en el eje-y
representamos cada observación con un punto
y la variable gender para el color de cada punto.
Añadimos título al gráfico: “Mass vs. height of Starwars characters”
añadimos el subtítulo “Género de los personajes de starwars2”
etiquetamos los ejes x e y como “x =”Height (cm)“, y =”Weight (kg)“,
etiquetamos la leyenda”Gender”
añadimos la fuente de los datos.
Finalmente, usamos paleta con una escala de colores discreta que está diseñada para ser percibida por espectadores con formas comunes de daltonismo.

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = gender)) +
  geom_point() +
  labs(title = "Mass vs. height of Starwars characters",
       subtitle = "by gender",  
       x = "Height (cm)", y = "Weight (kg)",
       colour = "Gender",
       caption = "Source: dplyr package")  +
  scale_colour_viridis_d()

Aesthetics (estética) 🎯

ggplot(data = starwars2,
       mapping = aes(x = height,
                     y = mass,
                     colour = xxxxx,
                     shape = xxxxx,
                     size = xxxxx,
                     alpha = xxxxx)
       ) +
  geom_point() 

¿Cuál es la mejor representación gráfica? 🎯

¿Cómo son mis datos? 🤔

Un mundo de posibilidades 🤔

From data to viz project

Árbol de clasificación de gráficos según el tipo de datos

1. Distribución 🎯

Gráficos para distribuciones

1. Distribución 🎯

1.1 Histograma

Un histograma es una representación gráfica de una variable en forma de barras, donde la superficie de cada barra es proporcional a la frecuencia de los valores representados.

library(CDR)
head(renta_municipio_data)
                           Unidad  2019  2018  2017 2016 2015 codigo_ine
1                    44001 Ababuj    NA    NA    NA   NA   NA      44001
2      4400101 Ababuj distrito 01    NA    NA    NA   NA   NA    4400101
3 4400101001 Ababuj sección 01001    NA    NA    NA   NA   NA 4400101001
4                    40001 Abades 11429 10731 10314 9816 9904      40001
5      4000101 Abades distrito 01 11429 10731 10314 9816 9904    4000101
6 4000101001 Abades sección 01001 11429 10731 10314 9816 9904 4000101001
renta <- renta_municipio_data |>
   drop_na()
ggplot(renta, aes(`2019`)) +
  geom_histogram() #30 bins por defecto

1.1 Histograma

p_renta_2019 <- ggplot(renta, aes(`2019`)) +
  geom_histogram(color = "yellow", 
                 fill = "red", 
                 bins = nclass.Sturges(renta_municipio_data$`2019`)) +
labs(title = "Distribución de la renta por municipios",
       x = "Renta per cápita",
       y = "Frecuencia") +
theme_minimal()

p_renta_2019 

1.2 Gráfico de densidad

Una representación alternativa al histograma es la línea de densidad, que sustituye las barras por una línea continua, generalmente suavizada

p_renta_2019_density <- ggplot(renta, aes(`2019`)) +
  geom_density(fill = "blue",  alpha= 0.4) +
  labs(title = "Distribución de la renta por municipios",
       x = "Renta per cápita",
       y = "Densidad") +
  theme_minimal()

1.2 Gráfico de densidad e histograma

p_renta_2019_density + 
  geom_histogram(aes(y=..density..), 
                 fill = "yellow", color="blue", alpha = 0.4)

1.3 Boxplot vs. Violín

p <- renta |>
  ggplot(aes(x=0, y= `2019`)) 

boxplot <- p + 
  geom_boxplot(color = "red", fill = "pink") +
  theme_minimal()

violin <- p + geom_violin(color = "red", fill = "pink") +
  theme_minimal()

library(patchwork)
boxplot + violin

1.3 Violín

# con el objeto renta haz un gráfico de violin para la variable `2019`, 2018, 2017 en un mismo plot para que se puedan comparar

p <- renta |>
   ggplot(aes(x=0, y= `2017`))
 
p +
   geom_violin(aes(fill = "2017"), color = "red") +
   geom_violin(aes(x=1, y= `2018`, fill = "2018"), color = "blue") +
   geom_violin(aes(x=2, y= `2019`, fill = "2019"), color = "green") +
   theme_minimal() +
   scale_fill_manual(values = c("2017" = "red", "2018" = "blue", "2019" = "green")) +
#  pon en el eje x los años y en el eje y Renta per cápita  (euros)
   scale_x_continuous(breaks = c(0, 1, 2), labels = c("2017", "2018", "2019")) +
   labs(title = "Distribución de la renta por municipios",
       x = "Año",
       y = "Renta per cápita (euros)")

1.3 Violín

Cuando se trabaja en un proyecto de ciencia de datos, lo normal es tener tanto variables cualitativas como cuantitativas.

nox_madrid <-contam_mad |>  
  na.omit() |>  
  filter(nom_abv == "NOx") |> 
  filter(between(fecha, left = as.Date("2022-03-01"), right = as.Date("2022-03-31"))) 
  
 nox_madrid |> 
  ggplot(aes(zona, daily_mean)) +
  geom_violin() +
  geom_jitter(height = 0, width = 0.01) +
  aes(x = zona, y = daily_mean, fill =zona) +
  labs(title = "Distribución de NOx por zona",
       x = "Zona",
       y = "Concentración de NOx (µg/m3)") +
  theme_minimal()

1.3 Violín

library(ggstatsplot)

ggbetweenstats(
  data =nox_madrid,
  x = zona,
  y = daily_mean
)

1.2 Densidad

library(ggridges)
nox_madrid |> 
  ggplot(aes(x = daily_mean, y = zona, fill= zona)) +
  geom_density_ridges() +
  theme_ridges() + 
  labs(title = "Distribución de NOx por zona",
       x = "Concentración de NOx (µg/m3)",
       y = "Zona") +
  theme_minimal()

2. Distribución 🎯

Gráficos para correlaciones

2.1 Scatterplot

# Load data
demog_data <- read.table("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/multivariate.csv", header = T, sep = ";")
colnames(demog_data) <- gsub("\\.", "_", colnames(demog_data))
# scatterplot entre growth.rate y Life.expectancy
demog_data  |>
  ggplot(aes(x = Growth_rate, y = Life_expectancy)) +
  geom_point() +
  labs(title = "Esperanza de vida vs. Tasa de crecimiento",
       x = "Tasa de crecimiento",
       y = "Esperanza de vida") +
  theme_minimal()

2.1 Scatterplot

demog_data  |>
  ggplot(aes(x = Growth_rate, y = Life_expectancy, size=Pop,  color=Continent)) +
  geom_point(alpha=0.5) +
  scale_size(range = c(.1, 20)) +
  labs(title = "Esperanza de vida vs. Tasa de crecimiento",
       x = "Tasa de crecimiento",
       y = "Esperanza de vida") +
  theme_minimal()

2.1 Scatterplot

demog_scatter <- ggplot(data = demog_data, # data
              aes(x = Growth_rate, y = Life_expectancy, colour = Continent)) + #aesthetics
    geom_point(alpha = 0.5) + #geometries
    facet_wrap(~ Continent, scales = "free") + #facets
    labs(title = "Life expectancy at birth vs. Per capita GDP", 
         x = "Growth_rate", y = "Life_expectancy", colour = "Continent") + #labels
    theme_light() #themes

demog_scatter 

2.1 Scatterplot

demog_scatter + geom_smooth(method = NULL, se = T) 

2.1 Scatterplot matrix

# con las variables cuantitativas el conjunto de datos demog_data haz una scatterplot matrix
library(GGally)

demo_cuanti <- demog_data |>
   select(Pop, Birth_rate, Mortality_rate, Life_expectancy, Infant_mortality, 
         Children_per_woman,  Growth_rate, Population_aged_65_) |>
   na.omit()

demo_cuanti |> 
   ggpairs()

Correlation matrix

# con las variables cuantitativas el conjunto de datos demog_data haz una matriz de correlación
demo_cuanti |> 
  cor() |> 
  corrplot:: corrplot.mixed( order = 'AOE')

2.2 Heatmap

heatmap(as.matrix(scale(demo_cuanti)), col = topo.colors(256), cexCol =  0.5) 

Otros gráficos interesantes 🎯

Hexbin plot

ggplot(data = starwars2, mapping = aes(x = height, y = mass)) +
  geom_hex() 

Density

ggplot(data = starwars2, aes(x = height)) +
  geom_density()

Density con filter()

starwars2 |>
  filter(gender %in% c("feminine", "masculine")) |>
  ggplot(aes(height, fill = gender)) +
  geom_density()

Density con facet

starwars |>
  mutate(human = case_when(species == "Human" ~ "Human",
                           species != "Human" ~ "Not Human")) |>
  filter(gender %in% c("feminine", "masculine"), !is.na(human)) |>
  ggplot(aes(height, fill = gender)) +
  facet_grid(. ~ human) + #<--- this is a formula
  geom_density()

Gráficos interactivos 🎯

La función ggplotly() de {plotly}

El paquete leaflet

library(leaflet)
leaflet() |>
  addTiles() |>  
  addMarkers(lng= -5.9925707869459455, lat= 37.38633182455925, popup="La Giralda de Sevilla")
# library(leaflet)
leaflet() |>
  addTiles() |>  
  addProviderTiles("Esri.WorldImagery") |>
  addMarkers(lng= -5.9925707869459455, lat= 37.38633182455925, popup="La Giralda de Sevilla")

Recursos interesantes

https://r-graph-gallery.com/boxplot.html

The R graph gallery

https://r-charts.com/

R charts

https://exts.ggplot2.tidyverse.org/gallery/

Extensiones {ggplot2}

Nuestra filosofía 📖: learning by doing

  • Recuerda que los ordenadores actualmente no son inteligentes.

  • Filosofía: copy, paste, and tweak

  • La mejor forma de aprender código es haciéndolo.

  • La práctica es la clave

4. Conceptos clave 📖

  • {ggplot2}.

  • geom_xxx().

Referencias

Wilkinson, Leland. 2012. The Grammar of Graphics. Springer.